﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.ComponentModel;

using Noris.Tools.FrxEditor.ComponentsLayer;
using Noris.Tools.FrxEditor.Components;
using Noris.Tools.FrxEditor.Data;
using Noris.Schedule.Support;

namespace Noris.Tools.FrxEditor.Editor
{
	/// <summary>
	/// Bázová třída pro vizuální položky v editoru.
	/// Potomkové jsou ReportItem a z něj pak ReportComponent (textbox, label, atd), a ReportBand (vykresluje záhlaví svazku).
	/// Další potomek je EditorSplitter.
	/// Nejde o grafický objekt odvozený z Control, ale o datový objekt doplněný o metody pro vykreslování.
	/// Tento objekt obsahuje víceméně popisné property určující vzhled a umístění položky na sestavě a chování v editoru.
	/// Tato bázová třída řeší fyzickou podporu kreslení a interaktivity, ale konkrétní definice jsou implementovány na konkrétním potomkovi.
	/// </summary>
	public class EditorItem
	{
		#region Konstrukce a proměnné
        /// <summary>
        /// Bázová třída pro vizuální položky v editoru sestavy. Konstruktor.
        /// </summary>
		public EditorItem()
		{
			this.InteractivityMode = EditItemInteractivityMode.StandardActiveResize;
            this.LayerStandard = EditorLayer.Ground;
            this.LayerInteractive = EditorLayer.Ground;
		}
        /// <summary>
        /// Vrstva, v níž se nachází tento prvek ve standardním stavu (mimo interaktivní editaci).
        /// Implicitní pro třídu EditorItem je hodnota Ground.
        /// </summary>
        [Browsable(false)]
        public virtual int LayerStandard { get; set; }
        /// <summary>
        /// Vrstva, v níž se nachází tento prvek v interaktivním stavu (drag: move, resize)
        /// Implicitní pro třídu EditorItem je hodnota Ground.
        /// </summary>
        [Browsable(false)]
        public virtual int LayerInteractive { get; set; }
		#endregion
		#region Aktuální segment, mapa segmentů, tvorba segmentů, přidávání a vyhledání
		/// <summary>
		/// Bázová třída EditorItem nepřipravuje nic.
		/// Je věcí potomků, jaké segmenty deklarují.
		/// Bázová třída EditorItem nabízí metodu PrepareSegmentAddOne(...), která podporuje vkládání / aktualizaci segmentů.
		/// Na potomkovi tak nechává fyzickou deklaraci jejich typu a souřadnic.
		/// Příprava obnáší deklaraci segmentů = objekty třídy ActiveSegment, a jejich vkládání do metody this.AddActiveSegment().
		/// Příprava neprovádí vkládání segmentů do args, to dělá bázová třída EditorItem- mimo rámec této metody PrepareSegments().
		/// Příprava segmenty nekreslí.
		/// </summary>
		/// <param name="args"></param>
		protected virtual void PrepareSegments(EditPaintEventArgs args) { }
		/// <summary>
		/// Zkontroluje, zda v evidenci máme segment daného typu. 
		/// Pokud ne, založí jej do Dictionary i do List.
		/// Pokud ano, vloží do něj předané souřadnice, ale ne další vlastnosti (ty by se za života nemusely měnit).
		/// Segment přidá do args.
		/// </summary>
        /// <param name="owner"></param>
		/// <param name="segmentType">Typ segmentu = jeho klíč</param>
		/// <param name="interactiveSheet"></param>
		/// <param name="segmentPoint"></param>
		/// <param name="visualBound"></param>
		/// <param name="mouseCursor"></param>
		/// <param name="downCursor"></param>
		/// <param name="dragCursor"></param>
		/// <param name="gripType"></param>
		/// <param name="autoDragX"></param>
		/// <param name="autoDragY"></param>
		/// <param name="restrictRatio"></param>
		/// <param name="restrictXyCtrl"></param>
		/// <param name="restrictRatioCtrl"></param>
        /// <param name="drawOnAction"></param>
		/// <param name="args"></param>
        protected virtual void PrepareSegmentAddOne(object owner, IComparable segmentType, SheetF interactiveSheet, PointF segmentPoint, RectangleF visualBound,
            SysCursorType mouseCursor, SysCursorType downCursor, SysCursorType dragCursor, EditorItemSegmentGripType gripType,
            bool autoDragX, bool autoDragY, bool restrictRatio, bool restrictXyCtrl, bool restrictRatioCtrl,
            EditMouseChangeType drawOnAction,
            EditPaintEventArgs args)
        {
            this.PrepareSegmentAddOne(owner, segmentType, true, interactiveSheet, segmentPoint, visualBound,
                mouseCursor, downCursor, dragCursor, gripType,
                autoDragX, autoDragY, restrictRatio, restrictXyCtrl, restrictRatioCtrl,
                drawOnAction,
                args);
        }
		/// <summary>
		/// Zkontroluje, zda v evidenci máme segment daného typu. 
		/// Pokud ne, založí jej do Dictionary i do List.
		/// Pokud ano, vloží do něj předané souřadnice, ale ne další vlastnosti (ty by se za života nemusely měnit).
		/// Segment přidá do args.
		/// </summary>
        /// <param name="owner"></param>
		/// <param name="segmentType">Typ segmentu = jeho klíč. Tato hodnota vyhledá existující segment v mapě segmentů.</param>
        /// <param name="isActive">Má být segment aktivní? Tato hodnota se vždy aktualizuje.</param>
        /// <param name="interactiveSheet">Interaktivné souřadnice = kde segment reaguje na myš Tato hodnota se vždy aktualizuje.</param>
        /// <param name="segmentPoint">Klíčový bod segmentu = ten se pohybuje při pohybu myši Tato hodnota se vždy aktualizuje.</param>
        /// <param name="visualBound">Vizuální souřadnice, využívá potomek, nebo implicitní kreslení známých gripů. Tato hodnota se vždy aktualizuje.</param>
        /// <param name="mouseCursor">Kurzor při pohybu myši nad segmentem. Tato hodnota se vždy aktualizuje.</param>
        /// <param name="downCursor">Kurzor při pohybu myši nad segmentem. Tato hodnota se vždy aktualizuje.</param>
		/// <param name="dragCursor"></param>
		/// <param name="gripType"></param>
		/// <param name="autoDragX"></param>
		/// <param name="autoDragY"></param>
		/// <param name="restrictRatio"></param>
		/// <param name="restrictXyCtrl"></param>
		/// <param name="restrictRatioCtrl"></param>
        /// <param name="drawOnAction"></param>
		/// <param name="args"></param>
        protected virtual void PrepareSegmentAddOne(object owner, IComparable segmentType, bool isActive, SheetF interactiveSheet, PointF segmentPoint, RectangleF visualBound, 
			SysCursorType mouseCursor, SysCursorType downCursor, SysCursorType dragCursor, EditorItemSegmentGripType gripType,
			bool autoDragX, bool autoDragY, bool restrictRatio, bool restrictXyCtrl, bool restrictRatioCtrl, 
			EditMouseChangeType drawOnAction,
			EditPaintEventArgs args)
		{
			if (this.AllSegmentList == null) this.AllSegmentList = new List<EditorItemSegment>();

			EditorItemSegment segment = new EditorItemSegment(owner);
            segment.SegmentType = segmentType;
            segment.IsActive = isActive;
			segment.SegmentInteractiveSheet = interactiveSheet;
			segment.SegmentPoint = segmentPoint;
			segment.MovedSegmentPoint = segmentPoint;
			segment.SegmentVisualBound = visualBound;
            segment.OnMouseMoveCursorType = mouseCursor;
            segment.OnMouseDownCursorType = downCursor;
            segment.OnMouseDragRequestCursorType = dragCursor;
            segment.GripType = gripType;
            segment.EnableAutoDragX = autoDragX;
            segment.EnableAutoDragY = autoDragY;
            segment.EnableRestrictRatio = restrictRatio;
            segment.EnableAutoRestrictXYbyCtrl = restrictXyCtrl;
            segment.EnableAutoRestrictRatioByCtrl = restrictRatioCtrl;
            segment.DrawOnAction = drawOnAction;

			this.AllSegmentList.Add(segment);
        }
		/// <summary>
		/// Poté, co jsou naplněny aktivní segmenty do this, překopíruje je i do argumentu args, 
		/// aby se z nich staly aktivní prostory v editoru
		/// </summary>
		/// <param name="args"></param>
		protected void StoreActiveSegmentsInto(EditPaintEventArgs args)
		{
			if (this.AllSegmentList != null && this.AllSegmentList.Count > 0)
			{
                bool hasInteractive = false;
                foreach (EditorItemSegment segment in this.ActiveSegmentList)
                {
                    args.ActiveVisualAreaList.Add(segment.SegmentInteractiveSheet);
                    hasInteractive = true;
                }
                args.IsInteractive = hasInteractive;
				args.IsVisible = true;
			}
		}
		/// <summary>
		/// Najde a vrátí aktivní segment, který se nachází na dané souřadnici.
		/// Hledá v seznamu this.ActiveSegmentList, od konce (od prvků, které jsou na ose ZOrder navrchu)
		/// </summary>
        /// <param name="pointF"></param>
		/// <returns></returns>
		protected EditorItemSegment FindActiveSegment(PointF pointF)
		{
			if (this.AllSegmentList != null && this.AllSegmentList.Count > 0)
			{
				for (int s = this.AllSegmentList.Count - 1; s >= 0; s--)
				{
					EditorItemSegment segment = this.AllSegmentList[s];
					if (segment.IsActive && segment.SegmentInteractiveSheet.Contains(pointF))
						return segment;
				}
			}
			return null;
		}
        /// <summary>
        /// Souhrn všech segmentů tohoto prvku.
        /// Pořadí = odspodu, tak jak se kreslí. Nejvýše položený segment (celý viditelný) je v kolekci ten poslední.
        /// Aktivní segmenty deklaruje konkrétní potomek, podle toho jak je potřebuje.
        /// </summary>
        [Browsable(false)]
        public IEnumerable<EditorItemSegment> Segments { get { return this.AllSegmentList; } }
        /// <summary>
		/// Souhrn všech segmentů tohoto prvku.
		/// Pořadí = odspodu, tak jak se kreslí.
		/// Vyhledání aktivního prvku se provádí od konce (tak, jak jsou viditelné).
		/// Aktivní segmenty deklaruje konkrétní potomek, podle toho jak je potřebuje.
		/// </summary>
		[Browsable(false)]
		protected List<EditorItemSegment> AllSegmentList { get; set; }
        /// <summary>
        /// Souhrn aktivních segmentů tohoto prvku (které mají IsActive = true).
        /// Pořadí = odspodu, tak jak se kreslí.
        /// Vyhledání aktivního prvku se provádí od konce (tak, jak jsou viditelné).
        /// Aktivní segmenty deklaruje konkrétní potomek, podle toho jak je potřebuje.
        /// </summary>
        protected IEnumerable<EditorItemSegment> ActiveSegmentList { get { return this.AllSegmentList.Where(s => s.IsActive); } }
		/// <summary>
		/// Aktuální stav myši v rámci elementu
		/// </summary>
		[Browsable(false)]
        public EditMouseState CurrentMouseState { get; protected set; }
		/// <summary>
		/// True, pokud na tomto objektu je myš (OnControl nebo MouseLeftDown nebo MouseRightDown)
		/// </summary>
		[Browsable(false)]
        public bool IsWithMouse { get { return (this.CurrentMouseState == EditMouseState.OnControl || this.IsWithMouseDown || this.IsWithMouseInteractive); } }
		/// <summary>
        /// True, pokud na tomto objektu je zmáčknutá myš
        /// (CurrentMouseState je MouseLeftDown nebo MouseRightDown)
		/// </summary>
		[Browsable(false)]
        public bool IsWithMouseDown { get { return (this.CurrentMouseState == EditMouseState.MouseLeftDown || this.CurrentMouseState == EditMouseState.MouseRightDown); } }
        /// <summary>
        /// True, pokud tento objekt je nyní přesouván nebo resizován myší
        /// (CurrentMouseState je MouseDragMove nebo MouseDragResize).
        /// </summary>
        [Browsable(false)]
        public bool IsWithMouseInteractive { get { return (this.CurrentMouseState == EditMouseState.MouseDragMove || this.CurrentMouseState == EditMouseState.MouseDragResize); } }
        /// <summary>
        /// True, pokud tento objekt má být zobrazen na interaktivní vrstvě.
        /// true : pokud objekt je IsWithMouseDown nebo IsWithMouseInteractive 
        /// (CurrentMouseState je MouseLeftDown nebo MouseRightDown nebo MouseDragMove nebo MouseDragResize)
        /// false : pokud není myš stisknuta (CurrentMouseState je OnControl nebo Outside nebo None)
        /// </summary>
        [Browsable(false)]
        public virtual bool IsOnInteractiveLayer { get { return (this.IsWithMouseDown || this.IsWithMouseInteractive); } }
        /// <summary>
        /// True, pokud pro tento objekt mají být kresleny všechny jeho interaktivní segmenty
        /// </summary>
        [Browsable(false)]
        public virtual bool IsDrawSegments { get { return (this.IsWithMouse || this.IsWithMouseInteractive); } }
		/// <summary>
		/// Segment, který je v dané chvíli aktivní (je na něm myš, nebo se s ním někam táhne)
		/// </summary>
		[Browsable(false)]
		public EditorItemSegment CurrentActiveSegment { get; protected set; }
		/// <summary>
		/// Property udává možnosti interaktivity tohoto prvku.
		/// Na základě hodnoty v této property pak prvek deklaruje aktivní segmenty a jejich vlastnosti, 
		/// viz metoda this.PrepareSegments().
		/// Bázová třída obsahuje hodnotu StandardActiveResize, 
		/// potomek ji může předefinovat (property je virtual), a bázová třída pak v metodě PrepareSegments() deklaruje odpovídající segmenty.
		/// </summary>
		[Browsable(false)]
		public virtual EditItemInteractivityMode InteractivityMode { get; set; }
		/// <summary>
		/// Příznak zamčené položky.
		/// Zamčená položka nemůže provádět Move ani Resize.
		/// </summary>
		[Browsable(false)]
		public bool IsLocked { get; set; }
		/// <summary>
		/// Je tento prvek v průběhu editace interaktivní?
		/// true = jeho InteractivityMode obsahuje nahozený některý bit (kterýkoli), false = neobsahuje nic (=None).
		/// </summary>
		[Browsable(false)]
		protected virtual bool IsInteractive { get { return (this.HasInteractiveModeAny(EditItemInteractivityMode.All)); } }
        /// <summary>
        /// Obsahuje číslo vrstvy, ve které se prvek vyskytuje aktuálně, podle stavu this.IsOnInteractiveLayer
        /// </summary>
        [Browsable(false)]
        public int CurrentLayer
        {
            get
            {
                if (this.IsOnInteractiveLayer)
                    return this.LayerInteractive;
                return this.LayerStandard;
            }
        }
		/// <summary>
		/// Má tento element povolené všechny dotazované režimy interaktivity?
		/// Pokud jich je zadáno více, musí je mít všechny (jeden ze zadaných nestačí).
		/// Například: pokud testuji režim Resize (což je ResizeX + ResizeY), 
		/// pak vrací true jen tehdy, když současný režim InteractivityMode obsahuje oba bity, tedy jak ResizeX, tak i ResizeY.
		/// Existuje párová metoda, HasInteractiveModeAny, která by vrátila true i jen pro bit ResizeX.
		/// </summary>
		/// <param name="interactivityTest"></param>
		/// <returns></returns>
		protected bool HasInteractiveModeAll(EditItemInteractivityMode interactivityTest)
		{
			int currMode = (int)this.InteractivityMode;
			int testMode = (int)interactivityTest;
			return ((currMode & testMode) == testMode);
		}
		/// <summary>
		/// Má tento element alespoň jeden dotazovaný režim interaktivity?
		/// Pokud jich je zadáno více, stačí jeden z nich..
		/// Například: pokud testuji režim Resize (což je ResizeX + ResizeY), 
		/// pak vrací true pro režimy jak ResizeX, tak i ResizeY, tak i pro Resize.
		/// Existuje párová metoda, HasInteractiveModeAll, která by vrátila true jen pokud by současný režim byl (ResizeX a současně ResizeY).
		/// </summary>
		/// <param name="interactivityTest"></param>
		/// <returns></returns>
		protected bool HasInteractiveModeAny(EditItemInteractivityMode interactivityTest)
		{
			int currMode = (int)this.InteractivityMode;
			int testMode = (int)interactivityTest;
			return ((currMode & testMode) > 0);
		}
		#endregion
		#region Vizuální prostor, jeho získání, detekce viditelnosti (vizuální prostor se nachází ve viditelném rámci editoru)
		/// <summary>
		/// <para>
		/// Vrátí vizuální prostor tohoto prvku, v pixelech.
		/// Typicky by metoda měla určit vizuální souřadnice na základě svých souřadnic logických.
        /// Základní implementace ve třídě EditorItem vrací hodnotu z property CurrentVisualArea.
		/// </para>
		/// <para>
		/// Pokud vrátí null, pak prvek není viditelný.
		/// Metoda je volána pouze jedenkrát v rámci Draw().
		/// Vrácená hodnota bude uložena do this.CurrentVisualBound.
		/// Pokud bude vráceno null, neprovede se kreslení ani další operace (deklarace segmentů).
		/// </para>
		/// </summary>
		protected virtual RectangleF GetVisualBound(EditEventArgs args)
		{
			return this.CurrentVisualArea;
		}
		/// <summary>
		/// Nastaví vizuální prostor tohoto prvku.
		/// Typicky by metoda měla na základě předaných vizuálních souřadnic určit souřadnice logické a někam do sebe si je uchovat.
        /// Pokud bude zanedlouho volaná metoda this.GetVisualBound() (proto, že změnou rozměru dojde k překreslení editoru), 
        /// měly by se vracet pokud možno identické souřadnice (jaké byly sem zadány).
        /// Základní implementace ve třídě EditorItem nastavuje hodnotu do property CurrentVisualArea.
		/// </summary>
        protected virtual void SetVisualBound(EditEventArgs args, RectangleF visualArea)
		{
			this.CurrentVisualArea = visualArea;
		}
		/// <summary>
		/// Úložiště pro aktuální vizuální prostor tohoto prvku, v pixelech.
		/// Zde není implementována žádná logika, jde o obyčejné úložiště.
        /// Na těchto souřadnicích je objekt vykreslován a je interaktivní.
        /// Vedle toho v době editace existují ještě další dvě property: EditedInitialVisualArea a EditedCurrentVisualArea.
		/// </summary>
		[Browsable(false)]
        public RectangleF CurrentVisualArea { get; protected set; }
		/// <summary>
		/// Aktuálně je tento prvek ve viditelné oblasti (true = týká se jej zobrazování / false = je celý mimo)
		/// </summary>
		[Browsable(false)]
		protected bool CurrentIsVisible { get; set; }
        /// <summary>
        /// Resetuje svoje cachované VisualBounds.
        /// Bázová třída EditorItem resetuje property: StableCurrentVisualArea, EditedInitialVisualArea, EditedCurrentVisualArea.
        /// </summary>
        public virtual void ResetVisualBounds()
        {
            this.StableCurrentVisualArea = null;
            this.EditedInitialVisualArea = null;
            this.EditedCurrentVisualArea = null;
        }
        /// <summary>
        /// Vizuální souřadnice platné mimo editaci, po prvním výpočtu.
        /// Nuluje se při pohybech, při přípravě vizuálních dat.
        /// Setuje se v metodě pro určení vizuální souřadnice při jejím prvním běhu, když je null.
        /// Slouží jako cache.
        /// </summary>
        protected RectangleF? StableCurrentVisualArea { get; set; }
        /// <summary>
        /// Vizuální souřadnice platné v době editace (interaktivního přesouvání):
        /// výchozí souřadnice CurrentVisualArea před prvním přesunem.
        /// Mimo přesuny je zde null.
        /// </summary>
        protected RectangleF? EditedInitialVisualArea { get; set; }
        /// <summary>
        /// Vizuální souřadnice platné v době editace (interaktivního přesouvání):
        /// aktuální souřadnice CurrentVisualArea po každém jednotkovém drobném přesunu myší.
        /// Mimo přesuny je zde null.
        /// </summary>
        protected RectangleF? EditedCurrentVisualArea { get; set; }
		#endregion
		#region Interaktivita
        #region Explicitní aktivace segmentu (při Insertu prvku)
        /// <summary>
        /// Metoda má za úkol aktivovat určitý segment, na základě dodaného filtru.
        /// Metoda vrátí true, pokud byl hledaný segment nalezen a aktivován.
        /// </summary>
        /// <param name="predicate">Podmínka pro vyhledání segmentu</param>
        internal bool ActivateSegment(Func<EditorItemSegment, bool> predicate)
        {
            return this._ActivateSegment(predicate, null, false);
        }
        /// <summary>
        /// Metoda má za úkol aktivovat určitý segment, na základě dodaného filtru.
        /// Metoda vrátí true, pokud byl hledaný segment nalezen a aktivován.
        /// </summary>
        /// <param name="predicate">Podmínka pro vyhledání segmentu</param>
        /// <param name="setZeroOffset">Požadavek na nastavení ofsetu segmentu = 0, pro případné budoucí pohyby DragMove</param>
        internal bool ActivateSegment(Func<EditorItemSegment, bool> predicate, bool setZeroOffset)
        {
            return this._ActivateSegment(predicate, null, setZeroOffset);
        }
        /// <summary>
        /// Metoda má za úkol aktivovat určitý segment, na základě dodaného filtru.
        /// Metoda vrátí true, pokud byl hledaný segment nalezen a aktivován.
        /// </summary>
        /// <param name="predicate">Podmínka pro vyhledání segmentu</param>
        /// <param name="mouseDownPoint">Explicitně určený bod (od myši), ke kterému si má segment určit offset pro případné budoucí pohyby DragMove</param>
        internal bool ActivateSegment(Func<EditorItemSegment, bool> predicate, PointF? mouseDownPoint)
        {
            return this._ActivateSegment(predicate, mouseDownPoint, false);
        }
        /// <summary>
        /// Metoda má za úkol aktivovat určitý segment, na základě dodaného filtru.
        /// Metoda vrátí true, pokud byl hledaný segment nalezen a aktivován.
        /// </summary>
        /// <param name="predicate">Podmínka pro vyhledání segmentu</param>
        /// <param name="mouseDownPoint">Explicitně určený bod (od myši), ke kterému si má segment určit offset pro případné budoucí pohyby DragMove</param>
        /// <param name="setZeroOffset">Požadavek na nastavení ofsetu segmentu = 0, pro případné budoucí pohyby DragMove</param>
        private bool _ActivateSegment(Func<EditorItemSegment, bool> predicate, PointF? mouseDownPoint, bool setZeroOffset)
        {
            EditorItemSegment activeSegment = null;
            if (this.AllSegmentList != null && this.AllSegmentList.Count > 0)
            {
                for (int s = this.AllSegmentList.Count - 1; s >= 0; s--)
                {
                    EditorItemSegment segment = this.AllSegmentList[s];
                    if (predicate(segment))
                    {
                        activeSegment = segment;
                        break;
                    }
                }
            }
            this.CurrentActiveSegment = activeSegment;
            bool isActivated = (activeSegment != null);

            // Pokud máme segment, můžeme do něj nastavit offset myši proti segmentu, a připravit segment pro případné DragMove:
            if (isActivated)
            {
                if (mouseDownPoint.HasValue)
                    this.MouseDownPointSet(mouseDownPoint.Value);
                else if (setZeroOffset)
                    this.MouseDownPointSet(this.CurrentActiveSegment.SegmentPoint);
                else
                    this.MouseDownPointSet(this.CurrentActiveSegment.SegmentVisualBound.Location);
            }

            return isActivated;
        }
        /// <summary>
        /// Nastaví do this a do this.CurrentActiveSegment takové hodnoty, které odpovídají stisknutí myši na daném bodě.
        /// V budoucnu tyto uložené hodnoty umožní korektně přesouvat aktivní segment.
        /// </summary>
        /// <param name="mouseDownPoint"></param>
        private void MouseDownPointSet(PointF mouseDownPoint)
        {
            if (this.CurrentActiveSegment == null) return;

            // Stisknutí myši způsobí, že si zapamatujeme offset myši proti segmentu 
            //  (abychom pak segmentem mohli v případě potřeby pohybovat korektně = za právě zmáčknuté místo):
            this.MouseDownOffset = mouseDownPoint.GetRelativePointFromBase(this.CurrentActiveSegment.SegmentPoint);
            this.MouseDownVisualBounds = this.CurrentVisualArea;
            this.CurrentActiveSegment.MovedSegmentPoint = this.CurrentActiveSegment.SegmentPoint;
        }
        #endregion
		#region Primární obsluha událostí myši
		/// <summary>
		/// Určí, zda daná aktivita myši vede k nějaké změně prvku
		/// </summary>
        /// <param name="args">Data o stavu (myš, interaktivita)</param>
		/// <returns></returns>
		public virtual void MouseChange(EditInteractiveEventArgs args)
		{
			if (!this.IsInteractive) return;

			switch (args.MouseChange)
			{
				case EditMouseChangeType.Enter:
					DetectChangeOnMouseEnter(args);
					break;
				case EditMouseChangeType.Move:
					DetectChangeOnMouseMove(args);
					break;
				case EditMouseChangeType.Leave:
					DetectChangeOnMouseLeave(args);
					break;
				case EditMouseChangeType.LeftDown:
					DetectChangeOnMouseLeftDown(args);
					break;
				case EditMouseChangeType.LeftDragBegin:
					DetectChangeOnMouseLeftDragBegin(args);
					break;
				case EditMouseChangeType.LeftDragMove:
					DetectChangeOnMouseLeftDragMove(args);
					break;
				case EditMouseChangeType.LeftDragEnd:
					DetectChangeOnMouseLeftDragEnd(args);
					break;
				case EditMouseChangeType.LeftUp:
					DetectChangeOnMouseLeftUp(args);
					break;
			}
		}
        /// <summary>
		/// Vrací true, pokud daný stav (state) obsahuje alespoň jeden z daných stavů (state OR test &gt; 0).
		/// </summary>
		/// <param name="state">aktuální stav</param>
		/// <param name="tests">testované hodnoty</param>
		/// <returns></returns>
		protected bool StateContainAnyOf(EditMouseChangeType state, params EditMouseChangeType[] tests)
		{
			foreach (EditMouseChangeType test in tests)
			{
				if ((state & test) > 0)
					return true;
			}
			return false;
		}
		/// <summary>
		/// Myš vstoupila do prostoru zdejšího prvku
		/// </summary>
		/// <param name="args"></param>
		protected virtual void DetectChangeOnMouseEnter(EditInteractiveEventArgs args)
		{
            int layerBefore = this.CurrentLayer;
            bool reDraw = false;
			this.CurrentMouseState = EditMouseState.OnControl;

			this.CurrentActiveSegment = this.FindActiveSegment(args.MousePointF);
			if (this.CurrentActiveSegment != null)
			{
                Steward.Diagnostic.InfoAddLineWithTime("EditorItem.MouseEnter(), to segment (%0)", this.CurrentActiveSegment.GripType.ToString());
                args.CurrentActiveSegment = this.CurrentActiveSegment;
                this.OnSegmentMouseEnter(args);
				if (this.StateContainAnyOf(this.CurrentActiveSegment.DrawOnAction, EditMouseChangeType.Enter))
                    reDraw = true;
				args.NeedCursor = this.CurrentActiveSegment.OnMouseMoveCursorType;

                if (reDraw)
                    args.ReDrawLayers(layerBefore, this.CurrentLayer);   // Property CurrentLayer (odvozená od IsOnInteractiveLayer) reaguje na stav CurrentMouseState
			}
            else
	        {
                Steward.Diagnostic.InfoAddLineWithTime("EditorItem.MouseEnter(), to NULL segment");
            }
		}
		/// <summary>
		/// Myš se pohybuje nad zdejším prvkem
		/// </summary>
		/// <param name="args"></param>
		protected virtual void DetectChangeOnMouseMove(EditInteractiveEventArgs args)
		{
			this.CurrentMouseState = EditMouseState.OnControl;
			EditorItemSegment segment = this.FindActiveSegment(args.MousePointF);
			if (EditorItemSegment.IsEqual(segment, this.CurrentActiveSegment))
			{	// Není změna, stále jsem na stejném segmentu:
				if (this.CurrentActiveSegment != null)
				{
                    Steward.Diagnostic.InfoAddLineWithTime("EditorItem.MouseMove(), equal NOT null segment (%0)", (segment == null ? "null" : segment.GripType.ToString()));
                    args.CurrentActiveSegment = this.CurrentActiveSegment;
                    this.OnSegmentMouseMove(args);
                    if (this.StateContainAnyOf(this.CurrentActiveSegment.DrawOnAction, EditMouseChangeType.Move))
                        args.ReDrawLayer(this.LayerStandard);
					args.NeedCursor = this.CurrentActiveSegment.OnMouseMoveCursorType;
				}
                else
                {
                    Steward.Diagnostic.InfoAddLineWithTime("EditorItem.MouseMove(), equal NULL segment (%0)", (segment == null ? "null" : segment.GripType.ToString()));
                }
			}
			else
			{	// Změna segmentu:
				if (this.CurrentActiveSegment != null)
				{
                    Steward.Diagnostic.InfoAddLineWithTime("EditorItem.MouseMove(), change segment, old NOT null (%0)", this.CurrentActiveSegment.GripType.ToString());
                    args.CurrentActiveSegment = this.CurrentActiveSegment;
                    this.OnSegmentMouseLeave(args);
					if (this.StateContainAnyOf(this.CurrentActiveSegment.DrawOnAction, EditMouseChangeType.Leave))
                        args.ReDrawLayer(this.LayerStandard);
					args.NeedCursor = SysCursorType.Default;
					this.CurrentActiveSegment = null;
				}
                else
                {
                    Steward.Diagnostic.InfoAddLineWithTime("EditorItem.MouseMove(), change segment, old segment is NULL");
                }

				this.CurrentActiveSegment = segment;

				if (segment != null)
				{
                    Steward.Diagnostic.InfoAddLineWithTime("EditorItem.MouseMove(), change segment, new segment NOT null (%0)", segment.GripType.ToString());
                    args.CurrentActiveSegment = this.CurrentActiveSegment;
                    this.OnSegmentMouseEnter(args);
					this.OnSegmentMouseMove(args);
					if (this.StateContainAnyOf(this.CurrentActiveSegment.DrawOnAction, EditMouseChangeType.Enter, EditMouseChangeType.Move))
                        args.ReDrawLayer(this.LayerStandard);
					args.NeedCursor = this.CurrentActiveSegment.OnMouseMoveCursorType;
				}
                else
                {
                    Steward.Diagnostic.InfoAddLineWithTime("EditorItem.MouseMove(), change segment, new segment is NULL");
                }
            }
		}
		/// <summary>
		/// Řeší událost, kdy se stiskla myš
		/// </summary>
		/// <param name="args"></param>
		protected virtual void DetectChangeOnMouseLeftDown(EditInteractiveEventArgs args)
		{
			this.CurrentMouseState = EditMouseState.MouseLeftDown;
			EditorItemSegment segment = this.FindActiveSegment(args.MousePointF);
			if (!EditorItemSegment.IsEqual(segment, this.CurrentActiveSegment))
			{	// Je změna segmentu: musím zajistit OldSegmentLeave a CurrSegmentEnter:
				if (this.CurrentActiveSegment != null)
				{
                    args.CurrentActiveSegment = this.CurrentActiveSegment;
					this.OnSegmentMouseLeave(args);
					if (this.StateContainAnyOf(this.CurrentActiveSegment.DrawOnAction, EditMouseChangeType.Leave))
                        args.ReDrawLayer(this.LayerStandard);
					args.NeedCursor = SysCursorType.Null;
					this.CurrentActiveSegment = null;
				}
				this.CurrentActiveSegment = segment;
				if (segment != null)
				{
                    args.CurrentActiveSegment = segment;
					this.OnSegmentMouseEnter(args);
					if (this.StateContainAnyOf(this.CurrentActiveSegment.DrawOnAction, EditMouseChangeType.Enter))
                        args.ReDrawLayer(this.LayerStandard);
                    args.NeedCursor = this.CurrentActiveSegment.OnMouseMoveCursorType;
				}
			}
			// Uložím si referenci na segment tak, jak vypadal v okamžiku MouseDown.
			// Ukládám si kopii (klon), protože následné překreslování vede k tomu, že do vstupního objektu segmentu se vepisují změny pozice,
			//   a to při Drag nemohu potřebovat:
			this.CurrentActiveSegment = (segment == null ? null : segment.Clone);
			segment = this.CurrentActiveSegment;             // Odsud obsahuje proměnná "segment" klon původního objektu.
			if (segment != null)
			{
                // Uložit do this a do this.CurrentActiveSegment hodnoty odpovídající bodu, kde se stiskla myš:
                this.MouseDownPointSet(args.MousePointF);

				this.OnSegmentMouseLeftDown(args);
				if (this.StateContainAnyOf(this.CurrentActiveSegment.DrawOnAction, EditMouseChangeType.LeftDown))
                    args.ReDrawLayer(this.LayerStandard);
				args.NeedCursor = this.CurrentActiveSegment.OnMouseDownCursorType;
			}
		}
		/// <summary>
		/// Zahájení přetahování aktuálního segmentu prvku myší.
		/// Tato událost signalizuje, že myš je zmáčknutá a pohybuje se, 
		/// a její současná pozice se vzdálila od bodu kde se zmáčkla o více než o málo.
		/// (Událost DetectChangeOnMouseLeftDown již proběhla.)
		/// </summary>
		/// <param name="args"></param>
		protected virtual void DetectChangeOnMouseLeftDragBegin(EditInteractiveEventArgs args)
		{
			// Aktivní segment byl určen v okamžiku MouseDown, teď jej hledat nesmíme (to je princip přetahování).
            EditorItemSegment segment = this.CurrentActiveSegment;
            if (segment != null && !this.IsLocked)
			{
                args.CurrentActiveSegment = segment;
                this.CurrentMouseState = ((segment.GripType == EditorItemSegmentGripType.Body) ? EditMouseState.MouseDragMove : EditMouseState.MouseDragResize);
                this.EditedInitialVisualArea = this.CurrentVisualArea;
                this.EditedCurrentVisualArea = this.CurrentVisualArea;
				this.OnSegmentMouseLeftDragBegin(args);
                if (this.StateContainAnyOf(segment.DrawOnAction, EditMouseChangeType.LeftDragBegin))
                {
                    args.ReDrawLayer(this.LayerStandard);
                    args.ReDrawLayer(this.LayerInteractive);
                }
                args.NeedCursor = segment.OnMouseDragRequestCursorType;
			}
		}
		/// <summary>
		/// Řeší přesouvání nebo resizování (pohyb myši při stisknutém levém buttonu).
		/// Pohyb již byl zahájen (událost DetectChangeOnMouseLeftDragBegin).
		/// </summary>
		/// <param name="args"></param>
		protected virtual void DetectChangeOnMouseLeftDragMove(EditInteractiveEventArgs args)
		{
			// Aktivní segment byl určen v okamžiku MouseDown, teď jej hledat nesmíme (to je princip přetahování).
            EditorItemSegment segment = this.CurrentActiveSegment;
            if (segment != null && !this.IsLocked)
			{
                args.CurrentActiveSegment = segment;
				segment.OnMouseDragRealCursorType = segment.OnMouseDragRequestCursorType;
				bool isChange = this.DragSegmentByMouse(args);
                if (isChange && segment.GripType != EditorItemSegmentGripType.None)
                {
                    RectangleF? movedVisualBound = this.DragSegmentGetVisualBound(args, segment.MovedSegmentPoint);
                    if (movedVisualBound.HasValue)
                    {
                        this.CurrentVisualArea = movedVisualBound.Value;
                        this.EditedCurrentVisualArea = movedVisualBound.Value;
                        args.ReDrawLayer(this.LayerInteractive);

                        this.OnSegmentMouseLeftDragMove(args);
                        if (this.StateContainAnyOf(segment.DrawOnAction, EditMouseChangeType.LeftDragMove))
                            args.ReDrawLayer(this.LayerInteractive);
                        args.NeedCursor = segment.OnMouseDragRealCursorType;
                    }
                }
			}
		}
		/// <summary>
		/// Ukončí přetahování aktuálního segmentu prvku myší.
		/// Tato událost signalizuje, že myš přetahovala segment, a právě byla uvolněna (zvedla se).
		/// (Události DetectChangeOnMouseLeftDragBegin + DetectChangeOnMouseLeftDragMove již proběhly,
		/// událost DetectChangeOnMouseLeftUp se nebude provádět).
		/// </summary>
		/// <param name="args"></param>
		protected virtual void DetectChangeOnMouseLeftDragEnd(EditInteractiveEventArgs args)
		{
			// Aktivní segment byl určen v okamžiku MouseDown, teď jej hledat nesmíme (to je princip přetahování).
            EditorItemSegment segment = this.CurrentActiveSegment;
            if (segment != null && !this.IsLocked)
			{
                args.CurrentActiveSegment = segment;
                this.OnSegmentMouseLeftDragEnd(args);
                this.CurrentMouseState = EditMouseState.OnControl;
                if (this.StateContainAnyOf(segment.DrawOnAction, EditMouseChangeType.LeftDragEnd))
                {
                    args.ReDrawLayer(this.LayerStandard);
                    args.ReDrawLayer(this.LayerInteractive);
                }
				args.NeedCursor = SysCursorType.Default;
			}
            this.EditedInitialVisualArea = null;
            this.EditedCurrentVisualArea = null;
            this.CurrentMouseState = EditMouseState.OnControl;
		}
		/// <summary>
		/// Řeší událost MouseLeftUp, která následuje po události MouseLeftDown, pokud mezitím nedošlo k MouseLeftDrag.
		/// Volá háčky OnSegmentMouseLeftUp a OnSegmentMouseLeftClick.
		/// </summary>
		/// <param name="args"></param>
		protected virtual void DetectChangeOnMouseLeftUp(EditInteractiveEventArgs args)
		{
			// Aktivní segment byl určen v okamžiku MouseDown, teď jej hledat nesmíme (to je princip MouseDown + MouseUp).
            EditorItemSegment segment = this.CurrentActiveSegment;
            if (segment != null)
			{
                args.CurrentActiveSegment = segment;
				this.OnSegmentMouseLeftUp(args);
				this.OnSegmentMouseLeftClick(args);
                if (segment.GripType == EditorItemSegmentGripType.Body)
                    this.OnBodyLeftClick(args);
				if (this.StateContainAnyOf(segment.DrawOnAction, EditMouseChangeType.LeftUp))
                    args.ReDrawLayer(this.LayerStandard);
				args.NeedCursor = SysCursorType.Default;
			}
			this.CurrentMouseState = EditMouseState.OnControl;
		}
		/// <summary>
		/// Řeší událost Mouse Leave
		/// </summary>
		/// <param name="args"></param>
		protected virtual void DetectChangeOnMouseLeave(EditInteractiveEventArgs args)
		{
            int layerBefore = this.CurrentLayer;
            bool reDraw = false;

            EditorItemSegment segment = this.CurrentActiveSegment;
            if (segment != null)
			{
                args.CurrentActiveSegment = segment;
                Steward.Diagnostic.InfoAddLineWithTime("EditorItem.MouseLeave(), from segment (%0)", this.CurrentActiveSegment.GripType.ToString());
                this.OnSegmentMouseLeave(args);
                if (this.StateContainAnyOf(segment.DrawOnAction, EditMouseChangeType.Leave))
                    reDraw = true;
				args.NeedCursor = SysCursorType.Default;
			}
            else
            {
                Steward.Diagnostic.InfoAddLineWithTime("EditorItem.MouseLeave(), from NULL segment");
            }
			this.CurrentMouseState = EditMouseState.Outside;
			this.CurrentActiveSegment = null;

            if (reDraw)
                args.ReDrawLayers(layerBefore, this.CurrentLayer);   // Property CurrentLayer (odvozená od IsOnInteractiveLayer) reaguje na stav CurrentMouseState
        }
		/// <summary>
		/// Offset pozice myši oproti bodu Location (počátek prostoru), v jehož rámci se myš stiskla.
		/// Tedy, o kolik doprava (X) a dolů (Y) je myš posunutá proti levému hornímu rohu odpovídajícího segmentu (nikoli nutně k celému elementu).
		/// </summary>
		[Browsable(false)]
		protected PointF MouseDownOffset;
		/// <summary>
		/// Vizuální souřadnice elementu v době, kdy přišla událost MouseDown (Left nebo Right). 
		/// Následný Drag posouvá/resizuje element počínaje z této výchozí pozice.
		/// </summary>
		[Browsable(false)]
		protected RectangleF MouseDownVisualBounds;
		#endregion
		#region Podpora pro přetahování segmentu pomocí myši
		/// <summary>
		/// Řeší přemisťování aktuálního segmentu pomocí myši.
		/// Tato metoda pouze určuje novou ideální souřadnici Gripu při akceptování podmínek aktuálního segmentu.
        /// Výsledek této metody je určení souřadnice segmentu do property this.CurrentActiveSegment.MovedSegmentPoint.
		/// Tato metoda neřeší fyzické přemístění segmentu ani změny pozice / rozměru this prvku, to řeší metoda this.MoveSegmentByMouse().
		/// Tato metoda vrací true = došlo k nějaké změně oproti posledně známému stavu (tj. je zapotřebí provést návazné změny).
		/// </summary>
		/// <param name="args"></param>
		protected virtual bool DragSegmentByMouse(EditInteractiveEventArgs args)
		{
			EditorItemSegment segment = this.CurrentActiveSegment;
			if (segment == null) return false;

			bool isCtrl = (System.Windows.Forms.Control.ModifierKeys == System.Windows.Forms.Keys.Control); // true = je stisknuta klávesa Ctrl
			bool isChange = false;
			// Určíme ideální souřadnici segmentu po jeho přesunutí (odpovídá souřadnici segment.SegmentPoint v okamžiku MouseDown):
			PointF mouseSegmentPoint = args.MousePointF.GetRelativePointFromBase(this.MouseDownOffset);     // Tam by chtěla jít myš
			PointF originSegmentPoint = segment.SegmentPoint;        // Tam začíná pohyb (výchozí bod)
			PointF targetSegmentPoint = originSegmentPoint;          // Cílový bod pohybu po uplatnění omezení

			// Aplikujeme omezení daná segmentem:
			if (segment.EnableAutoDragX && segment.EnableAutoDragY)
			{	// Povolení pohybu pro směr X i Y:
				if (segment.EnableAutoRestrictXYbyCtrl && isCtrl)
				{	// Automatické omezení pohybu X nebo Y, podle toho co je větší:
					float dx = Math.Abs(mouseSegmentPoint.X - originSegmentPoint.X);
					float dy = Math.Abs(mouseSegmentPoint.Y - originSegmentPoint.Y);
					if (dx >= dy)
					{
						targetSegmentPoint.X = mouseSegmentPoint.X;
                        segment.OnMouseDragRealCursorType = this.DragSegmentGetRealCursor(segment.OnMouseDragRequestCursorType, true, false);
					}
					else
					{
						targetSegmentPoint.Y = mouseSegmentPoint.Y;
                        segment.OnMouseDragRealCursorType = this.DragSegmentGetRealCursor(segment.OnMouseDragRequestCursorType, false, true);
					}
				}
				else if (segment.EnableRestrictRatio || (segment.EnableAutoRestrictRatioByCtrl && isCtrl))
				{	// Automatické omezení pohybu X nebo Y, tak aby výsledný poměr stran prvku byl stejný, jako byl výchozí poměr:
					RectangleF? movedVisualBound = this.DragSegmentGetVisualBound(args, mouseSegmentPoint);

				}
				else
				{	// Bez omezení pohybu:
					targetSegmentPoint.X = mouseSegmentPoint.X;
					targetSegmentPoint.Y = mouseSegmentPoint.Y;
                    segment.OnMouseDragRealCursorType = this.DragSegmentGetRealCursor(segment.OnMouseDragRequestCursorType, true, true);
                }
			}
			if (segment.EnableAutoDragX && !segment.EnableAutoDragY)
			{	// Povolení pohybu pouze pro směr X:
				targetSegmentPoint.X = mouseSegmentPoint.X;
                segment.OnMouseDragRealCursorType = this.DragSegmentGetRealCursor(segment.OnMouseDragRequestCursorType, true, false);
            }
			if (!segment.EnableAutoDragX && segment.EnableAutoDragY)
			{	// Povolení pohybu pouze pro směr Y:
				targetSegmentPoint.Y = mouseSegmentPoint.Y;
                segment.OnMouseDragRealCursorType = this.DragSegmentGetRealCursor(segment.OnMouseDragRequestCursorType, false, true);
			}

			if (targetSegmentPoint != segment.MovedSegmentPoint)
			{	// Nový bod je jinde než byl posledně:
				segment.MovedSegmentPoint = targetSegmentPoint;
				isChange = true;
			}
			return isChange;
		}
        /// <summary>
        /// Řeší automatické kurzory: namísto kurzoru typu Auto vrací kurzor odpovídající aktuálnímu stavu pohybu.
        /// </summary>
        /// <param name="requestCursorType"></param>
        /// <param name="moveX"></param>
        /// <param name="moveY"></param>
        /// <returns></returns>
        protected virtual SysCursorType DragSegmentGetRealCursor(SysCursorType requestCursorType, bool moveX, bool moveY)
        {
            SysCursorType realCursorType = requestCursorType;
            switch (requestCursorType)
            {
                case SysCursorType.NoMoveAuto:
                    if (moveX && moveY)
                        realCursorType = SysCursorType.NoMove2D;
                    else if (moveX)
                        realCursorType = SysCursorType.NoMoveHoriz;
                    else if (moveY)
                        realCursorType = SysCursorType.NoMoveVert;
                    else
                        realCursorType = SysCursorType.No;
                    break;
            }
            return realCursorType;
        }
		/// <summary>
		/// <para>
        /// Metoda určí vizuální souřadnice tohoto prvku poté, když je aktuální segment (this.CurrentActiveSegment)
		/// přemístěný na nové souřadnice (dané parametrem).
		/// Tato metoda nemění souřadnice prvku this, a ani nemění nic v aktuálním segmentu (this.CurrentActiveSegment),
		/// jen určí a vrátí souřadnice.
        /// </para>
        /// <para>
        /// Tato metoda je vhodná k využití v případě, kdy potomek chce zasahovat do změn rozměru a pozice prvku.
        /// </para>
        /// <para>
		/// Tato metoda neřeší omezení posunů na směr X, Y ani další omezení, tato omezení aplikují jiné metody.
		/// Pokud není vybrán žádný segment, anebo vybraný segment nepovoluje automatické přesuny, vrací null.
		/// Jinak vypočte nový rozměr prvku, a vrátí jej.
        /// </para>
		/// </summary>
		/// <param name="args"></param>
		/// <param name="movedSegmentPoint"></param>
		/// <returns></returns>
		protected virtual RectangleF? DragSegmentGetVisualBound(EditInteractiveEventArgs args, PointF movedSegmentPoint)
		{
			EditorItemSegment segment = this.CurrentActiveSegment;
			if (segment == null) return null;

			// Řešení rozměrů elementu podle typu segmentu:
			RectangleF original = this.MouseDownVisualBounds;
			RectangleF moved = original;
			float xl = original.X;
			float xr = original.Right;
			float yt = original.Y;
			float yb = original.Bottom;
			float xg = movedSegmentPoint.X;
			float yg = movedSegmentPoint.Y;
			switch (segment.GripType)
			{
				case EditorItemSegmentGripType.Body:
					moved = new RectangleF(movedSegmentPoint, original.Size);
					break;
				// Změnu velikosti provádím sestavením nového RectangleF s pomocí krajních souřadnic X,Y (Top,Right, Bottom,Left,Grip).
				// Využívám metodu DrawingExtensions.GetRectangleFFromDim s 5. parametrem minDist = 1f (minimální rozměr = 1 pixel).
				// Vždy zadávám souřadnici Gripu na druhém místě (x2, y2), protože ta je "pružná" (respektuje minDist).
				case EditorItemSegmentGripType.TopLeft:
					moved = DrawingExtensions.GetRectangleFFromDim(xr, xg, yb, yg, 1f);
					break;
				case EditorItemSegmentGripType.TopCenter:
					moved = DrawingExtensions.GetRectangleFFromDim(xl, xr, yb, yg, 1f);
					break;
				case EditorItemSegmentGripType.TopRight:
					moved = DrawingExtensions.GetRectangleFFromDim(xl, xg, yb, yg, 1f);
					break;
				case EditorItemSegmentGripType.MiddleRight:
					moved = DrawingExtensions.GetRectangleFFromDim(xl, xg, yt, yb, 1f);
					break;
				case EditorItemSegmentGripType.BottomRight:
					moved = DrawingExtensions.GetRectangleFFromDim(xl, xg, yt, yg, 1f);
					break;
				case EditorItemSegmentGripType.BottomCenter:
					moved = DrawingExtensions.GetRectangleFFromDim(xl, xr, yt, yg, 1f);
					break;
				case EditorItemSegmentGripType.BottomLeft:
					moved = DrawingExtensions.GetRectangleFFromDim(xr, xg, yt, yg, 1f);
					break;
				case EditorItemSegmentGripType.MiddleLeft:
					moved = DrawingExtensions.GetRectangleFFromDim(xr, xg, yt, yb, 1f);
					break;
                case EditorItemSegmentGripType.UserDefined:
                    return this.GetUserSegmentMoveResult(args, movedSegmentPoint);
				default:
					return null;
			}
			return moved;
		}
        /// <summary>
        /// Vyvolá háček this.OnUserSegmentDrag(), z něj získá příznak o výsledku pohybu user defined segmentu.
        /// </summary>
        /// <param name="args"></param>
        /// <param name="movedSegmentPoint"></param>
        /// <returns></returns>
        private RectangleF? GetUserSegmentMoveResult(EditInteractiveEventArgs args, PointF movedSegmentPoint)
        {
            EditInteractiveSegmentEventArgs segArgs = new EditInteractiveSegmentEventArgs(args, movedSegmentPoint, this.CurrentActiveSegment, this.MouseDownVisualBounds, this.CurrentVisualArea);
            this.OnUserSegmentMove(segArgs);
            if (segArgs.ReDrawInteractive)
                args.ReDrawLayer(EditorLayer.Interactive);
            if (segArgs.CurrentVisualBoundsChanged)
                return segArgs.CurrentVisualBounds;
            return null;
        }
		#endregion
		#region Virtuální prázdné háčky pro potomky
		/// <summary>
		/// Háček volaný při události Myš vstoupila na tento prvek na jeho určitý segment (this.CurrentActiveSegment).
		/// Bázová metoda ve třídě EditorItem je prázdná.
		/// </summary>
		/// <param name="args"></param>
		protected virtual void OnSegmentMouseEnter(EditInteractiveEventArgs args) { }
		/// <summary>
		/// Háček volaný při události Myš se pohybuje nad určitým segmentem (this.CurrentActiveSegment),
		/// nejde ale o změnu (ta je hlášena do události MouseEnter + MouseLeave).
		/// Bázová metoda ve třídě EditorItem je prázdná.
		/// </summary>
		/// <param name="args"></param>
		protected virtual void OnSegmentMouseMove(EditInteractiveEventArgs args) { }
		/// <summary>
		/// Háček volaný při události Levé tlačítko myši se stisklo na daném segmentu (this.CurrentActiveSegment).
		/// Ještě není jisté, zda se tlačítko zvedne (bude volán MouseLeftClick a MouseLeftUp), 
		/// anebo myš zůstane stiskutá a začne se pohybovat (pak se bude volat MouseLeftDragBegin, MouseLeftDragMove a LeftDragEnd, 
		/// ale ne MouseLeftClick a MouseLeftUp).
		/// Bázová metoda ve třídě EditorItem je prázdná.
		/// </summary>
		/// <param name="args"></param>
		protected virtual void OnSegmentMouseLeftDown(EditInteractiveEventArgs args) { }
        /// <summary>
        /// Háček volaný při události Levé tlačítko myši začíná přesouvat segment, na němž se nachází (this.CurrentActiveSegment).
        /// Bázová metoda ve třídě EditorItem je prázdná.
        /// </summary>
        /// <param name="args"></param>
		protected virtual void OnSegmentMouseLeftDragBegin(EditInteractiveEventArgs args) { }
        /// <summary>
        /// Háček volaný při události Levé tlačítko myši přesouvá segment, na němž se nachází (this.CurrentActiveSegment).
        /// Bázová metoda ve třídě EditorItem je prázdná.
        /// </summary>
        /// <param name="args"></param>
		protected virtual void OnSegmentMouseLeftDragMove(EditInteractiveEventArgs args) { }
        /// <summary>
        /// Háček volaný při události Levé tlačítko myši končí přesun segmentu, na němž se nachází (this.CurrentActiveSegment).
        /// Bázová metoda ve třídě EditorItem je prázdná.
        /// </summary>
        /// <param name="args"></param>
		protected virtual void OnSegmentMouseLeftDragEnd(EditInteractiveEventArgs args) { }
        /// <summary>
        /// Háček volaný při události Levé tlačítko myši se zvedlo, nebyl přesun (Drag).
        /// Bázová metoda ve třídě EditorItem je prázdná.
        /// </summary>
        /// <param name="args"></param>
		protected virtual void OnSegmentMouseLeftUp(EditInteractiveEventArgs args) { }
        /// <summary>
        /// Háček volaný při události Uživatel přemisťuje UserDefined segment.
        /// Bázová metoda ve třídě EditorItem je prázdná.
        /// </summary>
        /// <param name="args"></param>
        protected virtual void OnUserSegmentMove(EditInteractiveSegmentEventArgs args) { }
		/// <summary>
		/// Háček volaný při události Levé tlačítko myši se stisklo a uvolnilo, mezitím se nepohnulo. Klasický LeftClick.
        /// Metoda je volána pouze tehdy, pokud se kliklo na některý segment (tzn. this.CurrentActiveSegment není null).
		/// Bázová metoda ve třídě EditorItem je prázdná.
		/// </summary>
		/// <param name="args"></param>
		protected virtual void OnSegmentMouseLeftClick(EditInteractiveEventArgs args) { }
        /// <summary>
        /// Háček volaný při události Levé tlačítko myši se stisklo a uvolnilo, mezitím se nepohnulo. Klasický LeftClick.
        /// Metoda je volána pouze tehdy, pokud se kliklo na segment typu Body (this.CurrentActiveSegment.GripType == SegmentGripType.Body)
        /// Bázová metoda ve třídě EditorItem je prázdná.
        /// </summary>
        /// <param name="args"></param>
        protected virtual void OnBodyLeftClick(EditInteractiveEventArgs args) { }
        /// <summary>
        /// Háček volaný při události myš je zvednutá a opouští aktuální segment (this.CurrentActiveSegment).
        /// Bázová metoda ve třídě EditorItem je prázdná.
        /// </summary>
        /// <param name="args"></param>
		protected virtual void OnSegmentMouseLeave(EditInteractiveEventArgs args) { }
		#endregion
		#endregion
		#region Kreslení
		/// <summary>
		/// Vykreslení tohoto elementu.
		/// Tato metoda je volána zvenku.
		/// </summary>
		/// <param name="args"></param>
		public virtual void Paint(EditPaintEventArgs args)
		{
            this.DrawPrepareArea(args);
			if (!this.CurrentIsVisible) return;
            this.DrawPrepareSegments(args);
            this.DrawItem(args);
        }
        /// <summary>
        /// Provede přípravné práce před kreslením: určí CurrentVisualArea(), CurrentIsVisible().
        /// </summary>
        /// <param name="args"></param>
        protected virtual void DrawPrepareArea(EditPaintEventArgs args)
        {
			this.CurrentVisualArea = this.GetVisualBound(args);
			this.CurrentIsVisible = args.DetectVisible(this.CurrentVisualArea);
        }
        /// <summary>
        /// Pokud je (CurrentIsVisible == true), pak provede přípravu segmentů: PrepareSegments(), StoreActiveSegmentsInto().
        /// </summary>
        /// <param name="args"></param>
        protected virtual void DrawPrepareSegments(EditPaintEventArgs args)
        {
			if (!this.CurrentIsVisible) return;

			this.PrepareSegments(args);
			this.StoreActiveSegmentsInto(args);
        }
        /// <summary>
        /// Zajistí kompletní vykreslení prvku: pozadí, obsah, select, segmenty.
        /// Bázová třída EditorItem volá HasDrawBackground(), DrawBackground(), DrawContent(), DrawSelect(), HasDrawSegments(), DrawSegments(), DrawUserDefinedSegments().
        /// </summary>
        /// <param name="args"></param>
        protected virtual void DrawItem(EditPaintEventArgs args)
        {
            if (!this.CurrentIsVisible) return;

            if (this.HasDrawBackground(args))
    			this.DrawBackground(args);
			this.DrawContent(args);
			if (this.HasDrawSelect(args))
				this.DrawSelect(args);
            if (this.AllSegmentList != null)
            {
                if (this.HasDrawSegments(args))
                    this.DrawSegments(args);
                this.DrawUserDefinedSegments(args);
            }
        }
        /// <summary>
        /// Metoda určí, zda se má kreslit pozadí pod prvkem.
        /// Bázová metoda ve třídě EditorItem to povoluje vždy, potomek může převzít řízení a vracet true = kreslit / false = nekreslit.
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        protected virtual bool HasDrawBackground(EditPaintEventArgs args)
        {
            return true;
        }
		/// <summary>
        /// Metoda provede vykreslení pozadí prvku, pokud je povoleno metodou this.HasDrawBackground().
		/// Vizuální prostor prvku (v pixelech) je uložen v this.CurrentVisualBound.Value, není null.
		/// Bázová metoda ve třídě EditorItem je prázdná.
		/// </summary>
		/// <param name="args"></param>
		protected virtual void DrawBackground(EditPaintEventArgs args) { }
		/// <summary>
		/// Metoda provede vykreslení obsahu (popředí) prvku.
        /// Metoda je volána vždy, bez podmínek.
		/// Vizuální prostor prvku (v pixelech) je uložen v this.CurrentVisualBound.Value, není null.
		/// Bázová metoda ve třídě ¤ je prázdná.
		/// </summary>
		/// <param name="args"></param>
		protected virtual void DrawContent(EditPaintEventArgs args) { }
		/// <summary>
		/// Metoda určí, zda se má kreslit rámečku okolo prvku pro stav Selected.
		/// Bázová metoda ve třídě EditorItem to nepovoluje nikdy, povolit to musí override metoda na potomku.
		/// </summary>
		/// <param name="args"></param>
		/// <returns></returns>
		protected virtual bool HasDrawSelect(EditPaintEventArgs args)
		{
			return false;
		}
		/// <summary>
		/// Metoda provede vykreslení rámečku okolo prvku podle stavu prvku (selected).
		/// Vizuální prostor prvku (v pixelech) je uložen v this.CurrentVisualBound.Value, není null.
		/// Metoda je volaná pouze tehdy, když se má vykreslení skutečně provádět, to detekuje bázová třída EditorItem pomocí virtuální metody HasDrawSelect().
		/// Bázová metoda ve třídě EditorItem kreslí standardní rámeček okolo vizuálního rozměru.
		/// </summary>
		/// <param name="args"></param>
		protected virtual void DrawSelect(EditPaintEventArgs args)
		{
			if (!this.CurrentIsVisible) return;
			using (Pen pen = GetPenForSelection(args))
			{
				RectangleF bound = this.CurrentVisualArea;
				bound.X += (pen.Width - 0.5f);
				bound.Y += (pen.Width - 0.5f);
				bound.Width -= (pen.Width - 0.5f);
				bound.Height -= (pen.Width - 0.5f);
				args.Graphics.DrawRectangle(pen, Rectangle.Round(bound));
			}
		}
		/// <summary>
		/// Metoda určí, zda se mají kreslit segmenty (typicky Gripy).
		/// Bázová metoda ve třídě EditorItem povoluje jejich kreslení ve stavu s myší.
		/// </summary>
		/// <param name="args"></param>
		/// <returns></returns>
		protected virtual bool HasDrawSegments(EditPaintEventArgs args)
		{
			return (this.IsDrawSegments);
		}
		/// <summary>
		/// Metoda provede vykreslení všech segmentů deklarovaných v this.ActiveSegmentList.
		/// Vizuální prostor celého prvku (v pixelech) je uložen v this.CurrentVisualBound.Value, není null.
		/// Metoda je volaná pouze tehdy, když se mají segmenty skutečně kreslit, to detekuje bázová třída EditorItem pomocí virtuální metody HasDrawSegments().
		/// Bázová metoda ve třídě EditorItem kreslí standardní gripy, nekreslí segment typu Body 
		/// (to má kreslit metoda DrawBackground() a DrawContent).
		/// </summary>
		/// <param name="args"></param>
		protected virtual void DrawSegments(EditPaintEventArgs args)
		{
            foreach (EditorItemSegment segment in this.ActiveSegmentList)
				this.DrawSegmentOne(args, segment);
		}
		/// <summary>
		/// Metoda provede vykreslení jednoho segmentu deklarovaného v this.ActiveSegmentList.
		/// Vizuální prostor prvku (v pixelech) je uložen v this.CurrentVisualBound.Value, není null.
		/// Data segmentu jsou předána jako parametr.
		/// </summary>
		/// <param name="args"></param>
		/// <param name="segment"></param>
		protected virtual void DrawSegmentOne(EditPaintEventArgs args, EditorItemSegment segment)
		{
			switch (segment.GripType)
			{
				case EditorItemSegmentGripType.TopCenter:
				case EditorItemSegmentGripType.BottomCenter:
				case EditorItemSegmentGripType.MiddleLeft:
				case EditorItemSegmentGripType.MiddleRight:
					using (Brush brush = GetBrushForSegment(args, segment))
					{
                        args.Graphics.FillRectangle(brush, segment.SegmentVisualBound);
					}
					break;
				case EditorItemSegmentGripType.TopLeft:
				case EditorItemSegmentGripType.TopRight:
				case EditorItemSegmentGripType.BottomRight:
				case EditorItemSegmentGripType.BottomLeft:
					var state = args.Graphics.Save();
					args.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
					using (Brush brush = GetBrushForSegment(args, segment))
					{
                        args.Graphics.FillEllipse(brush, segment.SegmentVisualBound);
					}
					args.Graphics.Restore(state);
					break;
			}
		}
        /// <summary>
        /// Metoda projde segmenty, a pro segmenty typu UserDefined zavolá metodu DrawUserDefinedSegmentOne().
        /// </summary>
        /// <param name="args"></param>
        protected virtual void DrawUserDefinedSegments(EditPaintEventArgs args)
        {
            foreach (EditorItemSegment segment in this.ActiveSegmentList.Where(s => s.GripType == EditorItemSegmentGripType.UserDefined))
                this.DrawUserDefinedSegmentOne(args, segment);
        }
        /// <summary>
        /// Metoda je volána při kreslení prvku, docela na závěr všeho kreslení, a pouze pro segmenty typu (GripType == SegmentGripType.UserDefined).
        /// Potomek by si měl vykreslit UserDefined segmenty.
        /// Bázová metoda na třídě EditorItem je prázdná.
        /// </summary>
        /// <param name="args"></param>
        /// <param name="segment"></param>
        protected virtual void DrawUserDefinedSegmentOne(EditPaintEventArgs args, EditorItemSegment segment) { }
		#endregion
		#region Virtuální grafické nástroje
        /// <summary>
        /// Metoda vrátí nový štětec pro vykreslení obsahu segmentu.
        /// Pozor: štětec se použije v using patternu, po použití tedy bude disposován.
        /// </summary>
        /// <param name="args"></param>
        /// <param name="segment"></param>
        /// <returns></returns>
		protected virtual Brush GetBrushForSegment(EditPaintEventArgs args, EditorItemSegment segment)
		{
			return new SolidBrush(Color.Black);
		}
        /// <summary>
        /// Metoda vrátí nové pero pro kreslení rámečku Selection okolo vybraného prvku.
        /// Pozor: štětec se použije v using patternu, po použití tedy bude disposován.
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
		protected virtual Pen GetPenForSelection(EditPaintEventArgs args)
		{
			return new Pen(Color.Violet, 2f);
		}
		#endregion
	}
    #region class EditorItemSegment, enum EditorItemSegmentGripType
    /// <summary>
    /// EditorItemSegment : specifikace interaktivního prostoru (pozice, druh, kurzor, vlastnosti)
    /// </summary>
    public class EditorItemSegment
    {
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="owner"></param>
        public EditorItemSegment(object owner)
        {
            this._Owner = owner;
        }
        /// <summary>
        /// Vizualizace
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return this.GripType.ToString() + ": Point=" + this.SegmentPoint.ToString() + "; Bound=" + this.SegmentVisualBound.ToString();
        }
        private object _Owner;
        /// <summary>
        /// Unikátní identifikace typu segmentu.
        /// Má význam hlavně pro segment, jehož GripType = User, protože o takové segmenty se nestará Editor, ale prvek sám (detekce pohybu, resize, vykreslení segmentu)
        /// Pro základní segmenty (ne User) se sem typicky vkládá totéž, co do GripType.
        /// </summary>
        public IComparable SegmentType { get; set; }
        /// <summary>
        /// Je segment aktivní?
        /// Důvod: definici segmentů ponechávám v prvku i po změnách vlastností, měním právě jen vlastnosti (souřadnice a aktivitu).
        /// </summary>
        public bool IsActive { get; set; }
        /// <summary>
        /// Interaktivní prostor segmentu - v těchto souřadnicích bude segment reagovat na myš (jde samozřejmě o vizuální souřadnice v pixelech)
        /// </summary>
        public SheetF SegmentInteractiveSheet { get; set; }
        /// <summary>
        /// Hlavní bod segmentu, k němuž se vztahuje interaktivita.
        /// Pro segmenty typu Grip jde o bod základního prvku (hrana, vrchol), kterého se fyzicky týká přetahování.
        /// Pro segmenty typu Body jde o jejich Location, protože ta se přetahováním mění.
        /// Proces přetahování myší následně určí nové souřadnice tohoto bodu, na základě jeho posunu oproti výchozímu stavu platnému při MouseDown (offset myši při MouseDown).
        /// </summary>
        public PointF SegmentPoint { get; set; }
        /// <summary>
        /// Bod, odpovídající bodu SegmentPoint, ale posunutý o posun myši oproti původnímu stavu.
        /// </summary>
        public PointF MovedSegmentPoint { get; set; }
        /// <summary>
        /// Vizuální prostor segmentu - v těchto souřadnicích se bude segment vykreslovat.
        /// </summary>
        public RectangleF SegmentVisualBound { get; set; }
        /// <summary>
        /// Druh kurzoru, který má být aktivován při pohybu myši nad segmentem.
        /// </summary>
        public SysCursorType OnMouseMoveCursorType { get; set; }
        /// <summary>
        /// Druh kurzoru, který má být aktivován při zmáčknutí myši na segmentu.
        /// </summary>
        public SysCursorType OnMouseDownCursorType { get; set; }
        /// <summary>
        /// Druh kurzoru, který má být aktivován při tažení segmentu myší.
        /// Zde je uložen požadovaný druh kurzoru.
        /// Pokud je zde uložen kurzor typu *Auto, pak se vyhodnocuje v průběhu tažení se zohledněním aktuálních příznaků (Ctrl),
        /// a do OnMouseDragRealCursorType je uložen kurzor, odpovídající skutečnému stavu tažení (X nebo Y nebo 2D).
        /// </summary>
        public SysCursorType OnMouseDragRequestCursorType { get; set; }
        /// <summary>
        /// Druh reálného kurzoru, který má být aktivován při tažení segmentu myší.
        /// Vyhodnocuje se v metodě DragSegmentByMouse().
        /// </summary>
        public SysCursorType OnMouseDragRealCursorType { get; set; }
        /// <summary>
        /// Druh Gripu, který představuje tento segment.
        /// Pokud je jiný než None, pak se tímto povoluje automatický přesun nebo Resize podle dalších pravidel.
        /// </summary>
        public EditorItemSegmentGripType GripType { get; set; }
        /// <summary>
        /// Bázová třída může sama provádět Drag segmentu ve směru X
        /// </summary>
        public bool EnableAutoDragX { get; set; }
        /// <summary>
        /// Bázová třída může sama provádět Drag segmentu ve směru Y
        /// </summary>
        public bool EnableAutoDragY { get; set; }
        /// <summary>
        /// Bázová třída musí omezovat pohyb tohoto segmentu ve směru X a Y tak, aby byl dodržen výchozí poměr stran, 
        /// bez ohledu na tlačítko Ctrl.
        /// Používá se u segmentu typu ResizeXY = když se mění rozměr prvku v obou směrech (rohový úchyt).
        /// </summary>
        public bool EnableRestrictRatio { get; set; }
        /// <summary>
        /// Bázová třída může omezovat pohyb segmentu ve směru X nebo Y, pokud je stisknuto tlačítko Ctrl.
        /// Používá se u segmentu typu Body = když se přesouvá celý prvek.
        /// </summary>
        public bool EnableAutoRestrictXYbyCtrl { get; set; }
        /// <summary>
        /// Bázová třída může omezovat pohyb segmentu ve směru X a Y tak, aby byl dodržen poměr stran, 
        /// pokud je stisknuto tlačítko Ctrl.
        /// Používá se u segmentu typu ResizeXY = když se mění rozměr prvku v obou směrech (rohový úchyt).
        /// </summary>
        public bool EnableAutoRestrictRatioByCtrl { get; set; }
        /// <summary>
        /// Určuje, po které změně stavu myši má proběhnout Draw prvku.
        /// Enum MouseChangeState je charakteru Flags, jeho hodnoty lze sčítat, 
        /// nebo lze využít předdefinované kombinované hodnoty.
        /// </summary>
        public EditMouseChangeType DrawOnAction { get; set; }
        /// <summary>
        /// Vrací vždy nově vytvoření klon objektu this.
        /// </summary>
        public EditorItemSegment Clone
        {
            get
            {
                return (EditorItemSegment)this.MemberwiseClone();
            }
        }
        /// <summary>
        /// Vrací true, pokud dané dva segmenty obsahují shodný segment.
        /// Dva segmenty jsou shodné, pokud patří ke shodnému prvku (ObjectReference), a pokud mají shodnou hodnotu v GripType a SegmentType.
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool IsEqual(EditorItemSegment a, EditorItemSegment b)
        {
            if (a == null && b == null) return true;
            if (a == null || b == null) return false;
            return (Object.ReferenceEquals(a._Owner, b._Owner) &&
                    a.GripType == b.GripType &&
                    a.SegmentType.CompareTo(b.SegmentType) == 0);
        }
        /// <summary>
        /// Metoda nastaví property segmentu tak, aby na svém místě byl aktivní, ale neviditelný
        /// </summary>
        internal void SetInvisible()
        {
            this.SegmentVisualBound = RectangleF.Empty;
        }
    }
    /// <summary>
    /// Typ aktivní oblasti, umožňuje bázové třídě EditorItem provádět automatické změny pozice a velikosti prvku 
    /// na základě posunu odpovídajícího segmentu.
    /// </summary>
    public enum EditorItemSegmentGripType
    {
        /// <summary>Žádný</summary>
        None = 0,
        /// <summary>Tělo prvku (celá vizuální plocha)</summary>
        Body,
        /// <summary>Horní levý roh</summary>
        TopLeft,
        /// <summary>Horní střed</summary>
        TopCenter,
        /// <summary>Horní pravý roh</summary>
        TopRight,
        /// <summary>Pravý střed</summary>
        MiddleRight,
        /// <summary>Dolní pravý roh</summary>
        BottomRight,
        /// <summary>Dolní střed</summary>
        BottomCenter,
        /// <summary>Dolní levý roh</summary>
        BottomLeft,
        /// <summary>Levý střed</summary>
        MiddleLeft,
        /// <summary>
        /// Uživatelem používaný a definovaný segment.
        /// Bázová třída EditorItem se o něj nestará, jeho identifikace (IComparable) je uložena v ActiveSegment.SegmentType.
        /// Uživatel by jej měl vykreslovat optimálně v metodě IReportObject.DrawUserDefinedSegments(), na základě stavu myši.
        /// </summary>
        UserDefined
    }
    #endregion
    #region class EditInteractiveSegmentEventArgs : argumenty pro interaktivní metody s konkrétním segmentem v prostoru ViewPort
    /// <summary>
    /// EditInteractiveSegmentEventArgs : argumenty pro interaktivní metody s konkrétním segmentem v prostoru ViewPort
    /// </summary>
    public class EditInteractiveSegmentEventArgs : EditInteractiveEventArgs, IDisposable
    {
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="args"></param>
        /// <param name="movedSegmentPoint"></param>
        /// <param name="activeSegment"></param>
        /// <param name="mouseDownVisualBounds"></param>
        /// <param name="currentVisualBounds"></param>
        public EditInteractiveSegmentEventArgs(EditInteractiveEventArgs args, PointF movedSegmentPoint, EditorItemSegment activeSegment, RectangleF mouseDownVisualBounds, RectangleF currentVisualBounds)
            : base(args.ViewPortConvertor, args.ObjectState, args.MousePointF, args.MousePointFDown, args.MouseChange)
        {
            this.ActiveSegment = activeSegment;
            this.MouseDownVisualBounds = mouseDownVisualBounds;
            this._CurrentVisualBounds = currentVisualBounds;
            this._CurrentVisualBoundsOriginal = currentVisualBounds;
            this.ReDrawInteractive = false;
        }
        /// <summary>
        /// Aktivní segment
        /// </summary>
        public EditorItemSegment ActiveSegment { get; private set; }
        /// <summary>
        /// Souřadnice klíčového bodu segmentu po provedení aktuálního pohybu myši (ten bod, který odpovídá bodu segment.SegmentPoint)
        /// </summary>
        public PointF MovedSegmentPoint { get; private set; }
        /// <summary>
        /// Vizuální pozice celého objektu (ne jen aktuálního segmentu) v okamžiku stisknutí myši = zdejší souřadnice se nemění, ani když pohybem segmentu měním pozici objektu.
        /// </summary>
        public RectangleF MouseDownVisualBounds { get; private set; }
        /// <summary>
        /// Rozměry celého prvku před editací, i po editaci (je možno je změnit).
        /// Vložení nové hodnoty způsobí nastavení this.ReDrawInteractive na true.
        /// </summary>
        public RectangleF CurrentVisualBounds { get { return this._CurrentVisualBounds; } set { this._CurrentVisualBounds = value; this.ReDrawInteractive = true; } }
        private RectangleF _CurrentVisualBounds;
        private RectangleF _CurrentVisualBoundsOriginal;
        /// <summary>
        /// Příznak, že došlo k fyzické změně souřadnic CurrentVisualBounds.
        /// </summary>
        public bool CurrentVisualBoundsChanged { get { return (this._CurrentVisualBoundsOriginal != this._CurrentVisualBounds); } }
        /// <summary>
        /// Příznak, že došlo ke změně, která vyžaduje překreslení interaktivních objektů.
        /// Defaultně je false.
        /// Vložené hodnoty do CurrentVisualBounds nastaví na true.
        /// </summary>
        public bool ReDrawInteractive { get; set; }

    }
    #endregion
    #region enum EditItemInteractivityMode
    /// <summary>
    /// Informace o interaktivitě elementu = co s ním lze interaktivně provádět.
    /// Hodnoty lze sčítat, nebo lze využít hodnoty již kombinované.
    /// </summary>
    [Flags]
    public enum EditItemInteractivityMode
    {
        /// <summary>
        /// Element na myš nereaguje vůbec
        /// </summary>
        None = 0,
        /// <summary>
        /// Element reaguje na pohyb myši nad ním
        /// </summary>
        MouseOn = 1,
        /// <summary>
        /// Element lze selectovat
        /// </summary>
        Select = MouseOn << 1,
        /// <summary>
        /// Na element lze leftclicknout
        /// </summary>
        LeftClick = Select << 1,
        /// <summary>
        /// Na element lze doubleclicknout
        /// </summary>
        LeftDoubleClick = LeftClick << 1,
        /// <summary>
        /// Na element lze rightclicknout
        /// </summary>
        RightClick = LeftDoubleClick << 1,
        /// <summary>
        /// Standarní aktivita (MouseOn | Select | DoubleClick | RightClick)
        /// </summary>
        StandardResponse = MouseOn | Select | LeftClick | LeftDoubleClick | RightClick,
        /// <summary>
        /// Element lze posouvat ve směru X
        /// </summary>
        MoveX = RightClick << 1,
        /// <summary>
        /// Element lze posouvat ve směru Y
        /// </summary>
        MoveY = MoveX << 1,
        /// <summary>
        /// Element lze posouvat ve směru X i Y (jde o součet MoveX | MoveY)
        /// </summary>
        Move = MoveX | MoveY,
        /// <summary>
        /// Element lze zvětšovat a zmenšovat ve směru X (šířku)
        /// </summary>
        ResizeX = MoveY << 1,
        /// <summary>
        /// Element lze zvětšovat a zmenšovat ve směru Y (výšku)
        /// </summary>
        ResizeY = ResizeX << 1,
        /// <summary>
        /// Element lze zvětšovat a zmenšovat v obou směrech, libovolně
        /// </summary>
        Resize = ResizeX | ResizeY,
        /// <summary>
        /// Element může mít ukotvení ve směru X
        /// </summary>
        AnchorX = ResizeY << 1,
        /// <summary>
        /// Element může mít ukotvení ve směru Y
        /// </summary>
        AnchorY = AnchorX << 1,
        /// <summary>
        /// Element může mít ukotvení ve směru X nebo Y
        /// </summary>
        Anchor = AnchorX | AnchorY,
        /// <summary>
        /// Dodržovat výchozí poměr šířky a výšky (má význam pouze v kombinaci s hodnotou Resize)
        /// </summary>
        MaintainAspectRatio = AnchorY << 1,
        /// <summary>
        /// Element lze zvětšovat a zmenšovat v obou směrech, při změně rozměru se dodržuje výchozí poměr šířky a výšky
        /// </summary>
        ResizeMaintainAspectRatio = Resize | MaintainAspectRatio | Anchor,
        /// <summary>
        /// Standardní aktivita (StandardResponse), možnost přesouvat X + Y, Resize pouze X
        /// </summary>
        StandardActiveResizeX = StandardResponse | Move | ResizeX | Anchor,
        /// <summary>
        /// Standardní aktivita (StandardResponse), možnost přesouvat X + Y, Resize pouze Y
        /// </summary>
        StandardActiveResizeY = StandardResponse | Move | ResizeY | Anchor,
        /// <summary>
        /// Standardní aktivita (StandardResponse), možnost přesouvat X + Y, Resize X + Y (bez omezení)
        /// </summary>
        StandardActiveResize = StandardResponse | Move | Resize | Anchor,
        /// <summary>
        /// Standardní aktivita (StandardResponse), možnost přesouvat X + Y, Resize X + Y (s omezením poměru stran)
        /// </summary>
        StandardActiveResizeRatio = StandardResponse | Move | ResizeMaintainAspectRatio | Anchor,
        /// <summary>
        /// Souhrn všech možností včetně Anchor
        /// </summary>
        All = MouseOn | Select | LeftClick | LeftDoubleClick | RightClick | Move | Resize | Anchor | MaintainAspectRatio
    }
    #endregion
}
